home *** CD-ROM | disk | FTP | other *** search
/ Chip 2007 January, February, March & April / Chip-Cover-CD-2007-02.iso / Pakiet multimedia / Grafika i zdjecia / Edytory grafiki rastrowej i wektorowej / Inscape 0.44.1 / Inkscape-0.44.1-1.win32.exe / share / extensions / interp.py < prev    next >
Text File  |  2006-09-06  |  14KB  |  333 lines

  1. #!/usr/bin/env python 
  2. '''
  3. Copyright (C) 2005 Aaron Spike, aaron@ekips.org
  4.  
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2 of the License, or
  8. (at your option) any later version.
  9.  
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. GNU General Public License for more details.
  14.  
  15. You should have received a copy of the GNU General Public License
  16. along with this program; if not, write to the Free Software
  17. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  18. '''
  19. import inkex, cubicsuperpath, simplestyle, copy, math, re, bezmisc
  20.  
  21. uuconv = {'in':90.0, 'pt':1.25, 'px':1, 'mm':3.5433070866, 'cm':35.433070866, 'pc':15.0}
  22. def numsegs(csp):
  23.     return sum([len(p)-1 for p in csp])
  24. def interpcoord(v1,v2,p):
  25.     return v1+((v2-v1)*p)
  26. def interppoints(p1,p2,p):
  27.     return [interpcoord(p1[0],p2[0],p),interpcoord(p1[1],p2[1],p)]
  28. def pointdistance((x1,y1),(x2,y2)):
  29.     return math.sqrt(((x2 - x1) ** 2) + ((y2 - y1) ** 2))
  30. def bezlenapprx(sp1, sp2):
  31.     return pointdistance(sp1[1], sp1[2]) + pointdistance(sp1[2], sp2[0]) + pointdistance(sp2[0], sp2[1])
  32. def tpoint((x1,y1), (x2,y2), t = 0.5):
  33.     return [x1+t*(x2-x1),y1+t*(y2-y1)]
  34. def cspbezsplit(sp1, sp2, t = 0.5):
  35.     m1=tpoint(sp1[1],sp1[2],t)
  36.     m2=tpoint(sp1[2],sp2[0],t)
  37.     m3=tpoint(sp2[0],sp2[1],t)
  38.     m4=tpoint(m1,m2,t)
  39.     m5=tpoint(m2,m3,t)
  40.     m=tpoint(m4,m5,t)
  41.     return [[sp1[0][:],sp1[1][:],m1], [m4,m,m5], [m3,sp2[1][:],sp2[2][:]]]
  42. def cspbezsplitatlength(sp1, sp2, l = 0.5, tolerance = 0.001):
  43.     bez = (sp1[1][:],sp1[2][:],sp2[0][:],sp2[1][:])
  44.     t = bezmisc.beziertatlength(bez, l, tolerance)
  45.     return cspbezsplit(sp1, sp2, t)
  46. def cspseglength(sp1,sp2, tolerance = 0.001):
  47.     bez = (sp1[1][:],sp1[2][:],sp2[0][:],sp2[1][:])
  48.     return bezmisc.bezierlength(bez, tolerance)    
  49. def csplength(csp):
  50.     total = 0
  51.     lengths = []
  52.     for sp in csp:
  53.         lengths.append([])
  54.         for i in xrange(1,len(sp)):
  55.             l = cspseglength(sp[i-1],sp[i])
  56.             lengths[-1].append(l)
  57.             total += l            
  58.     return lengths, total
  59. def styleunittouu(string):
  60.     unit = re.compile('(%s)$' % '|'.join(uuconv.keys()))
  61.     param = re.compile(r'(([-+]?[0-9]+(\.[0-9]*)?|[-+]?\.[0-9]+)([eE][-+]?[0-9]+)?)')
  62.  
  63.     p = param.match(string)
  64.     u = unit.search(string)    
  65.     if p:
  66.         retval = float(p.string[p.start():p.end()])
  67.     else:
  68.         retval = 0.0
  69.     if u:
  70.         try:
  71.             return retval * uuconv[u.string[u.start():u.end()]]
  72.         except KeyError:
  73.             pass
  74.     return retval
  75.     
  76. def tweenstylefloat(property, start, end, time):
  77.     sp = float(start[property])
  78.     ep = float(end[property])
  79.     return str(sp + (time * (ep - sp)))
  80. def tweenstyleunit(property, start, end, time):
  81.     sp = styleunittouu(start[property])
  82.     ep = styleunittouu(end[property])
  83.     return str(sp + (time * (ep - sp)))
  84. def tweenstylecolor(property, start, end, time):
  85.     sr,sg,sb = parsecolor(start[property])
  86.     er,eg,eb = parsecolor(end[property])
  87.     return '#%s%s%s' % (tweenhex(time,sr,er),tweenhex(time,sg,eg),tweenhex(time,sb,eb))
  88. def tweenhex(time,s,e):
  89.     s = float(int(s,16))
  90.     e = float(int(e,16))
  91.     retval = hex(int(math.floor(s + (time * (e - s)))))[2:]
  92.     if len(retval)==1:
  93.         retval = '0%s' % retval
  94.     return retval
  95. def parsecolor(c):
  96.     r,g,b = '0','0','0'
  97.     if c[:1]=='#':
  98.         if len(c)==4:
  99.             r,g,b = c[1:2],c[2:3],c[3:4]
  100.         elif len(c)==7:
  101.             r,g,b = c[1:3],c[3:5],c[5:7]
  102.     return r,g,b
  103.  
  104. class Interp(inkex.Effect):
  105.     def __init__(self):
  106.         inkex.Effect.__init__(self)
  107.         self.OptionParser.add_option("-e", "--exponent",
  108.                         action="store", type="float", 
  109.                         dest="exponent", default=0.0,
  110.                         help="values other than zero give non linear interpolation")
  111.         self.OptionParser.add_option("-s", "--steps",
  112.                         action="store", type="int", 
  113.                         dest="steps", default=5,
  114.                         help="number of interpolation steps")
  115.         self.OptionParser.add_option("-m", "--method",
  116.                         action="store", type="int", 
  117.                         dest="method", default=2,
  118.                         help="method of interpolation")
  119.         self.OptionParser.add_option("-d", "--dup",
  120.                         action="store", type="inkbool", 
  121.                         dest="dup", default=True,
  122.                         help="duplicate endpaths")    
  123.         self.OptionParser.add_option("--style",
  124.                         action="store", type="inkbool", 
  125.                         dest="style", default=True,
  126.                         help="try interpolation of some style properties")    
  127.     def effect(self):
  128.         exponent = self.options.exponent
  129.         if exponent>= 0:
  130.             exponent = 1.0 + exponent
  131.         else:
  132.             exponent = 1.0/(1.0 - exponent)
  133.         steps = [1.0/(self.options.steps + 1.0)]
  134.         for i in range(self.options.steps - 1):
  135.             steps.append(steps[0] + steps[-1])
  136.         steps = [step**exponent for step in steps]
  137.             
  138.         paths = {}            
  139.         styles = {}
  140.         for id in self.options.ids:
  141.             node = self.selected[id]
  142.             if node.tagName =='path':
  143.                 paths[id] = cubicsuperpath.parsePath(node.attributes.getNamedItem('d').value)
  144.                 styles[id] = simplestyle.parseStyle(node.attributes.getNamedItem('style').value)
  145.             else:
  146.                 self.options.ids.remove(id)
  147.  
  148.         for i in range(1,len(self.options.ids)):
  149.             start = copy.deepcopy(paths[self.options.ids[i-1]])
  150.             end = copy.deepcopy(paths[self.options.ids[i]])
  151.             sst = copy.deepcopy(styles[self.options.ids[i-1]])
  152.             est = copy.deepcopy(styles[self.options.ids[i]])
  153.             basestyle = copy.deepcopy(sst)
  154.  
  155.             #prepare for experimental style tweening
  156.             if self.options.style:
  157.                 dostroke = True
  158.                 dofill = True
  159.                 styledefaults = {'opacity':'1.0', 'stroke-opacity':'1.0', 'fill-opacity':'1.0',
  160.                         'stroke-width':'1.0', 'stroke':'none', 'fill':'none'}
  161.                 for key in styledefaults.keys():
  162.                     sst.setdefault(key,styledefaults[key])
  163.                     est.setdefault(key,styledefaults[key])
  164.                 isnotplain = lambda x: not (x=='none' or x[:1]=='#')
  165.                 if isnotplain(sst['stroke']) or isnotplain(est['stroke']) or (sst['stroke']=='none' and est['stroke']=='none'):
  166.                     dostroke = False
  167.                 if isnotplain(sst['fill']) or isnotplain(est['fill']) or (sst['fill']=='none' and est['fill']=='none'):
  168.                     dofill = False
  169.                 if dostroke:
  170.                     if sst['stroke']=='none':
  171.                         sst['stroke-width'] = '0.0'
  172.                         sst['stroke-opacity'] = '0.0'
  173.                         sst['stroke'] = est['stroke'] 
  174.                     elif est['stroke']=='none':
  175.                         est['stroke-width'] = '0.0'
  176.                         est['stroke-opacity'] = '0.0'
  177.                         est['stroke'] = sst['stroke'] 
  178.                 if dofill:
  179.                     if sst['fill']=='none':
  180.                         sst['fill-opacity'] = '0.0'
  181.                         sst['fill'] = est['fill'] 
  182.                     elif est['fill']=='none':
  183.                         est['fill-opacity'] = '0.0'
  184.                         est['fill'] = sst['fill'] 
  185.  
  186.                     
  187.  
  188.             if self.options.method == 2:
  189.                 #subdivide both paths into segments of relatively equal lengths
  190.                 slengths, stotal = csplength(start)
  191.                 elengths, etotal = csplength(end)
  192.                 lengths = {}
  193.                 t = 0
  194.                 for sp in slengths:
  195.                     for l in sp:
  196.                         t += l / stotal
  197.                         lengths.setdefault(t,0)
  198.                         lengths[t] += 1
  199.                 t = 0
  200.                 for sp in elengths:
  201.                     for l in sp:
  202.                         t += l / etotal
  203.                         lengths.setdefault(t,0)
  204.                         lengths[t] += -1
  205.                 sadd = [k for (k,v) in lengths.iteritems() if v < 0]
  206.                 sadd.sort()
  207.                 eadd = [k for (k,v) in lengths.iteritems() if v > 0]
  208.                 eadd.sort()
  209.  
  210.                 t = 0
  211.                 s = [[]]
  212.                 for sp in slengths:
  213.                     if not start[0]:
  214.                         s.append(start.pop(0))
  215.                     s[-1].append(start[0].pop(0))
  216.                     for l in sp:
  217.                         pt = t
  218.                         t += l / stotal
  219.                         if sadd and t > sadd[0]:
  220.                             while sadd and sadd[0] < t:
  221.                                 nt = (sadd[0] - pt) / (t - pt)
  222.                                 bezes = cspbezsplitatlength(s[-1][-1][:],start[0][0][:], nt)
  223.                                 s[-1][-1:] = bezes[:2]
  224.                                 start[0][0] = bezes[2]
  225.                                 pt = sadd.pop(0)
  226.                         s[-1].append(start[0].pop(0))
  227.                 t = 0
  228.                 e = [[]]
  229.                 for sp in elengths:
  230.                     if not end[0]:
  231.                         e.append(end.pop(0))
  232.                     e[-1].append(end[0].pop(0))
  233.                     for l in sp:
  234.                         pt = t
  235.                         t += l / etotal
  236.                         if eadd and t > eadd[0]:
  237.                             while eadd and eadd[0] < t:
  238.                                 nt = (eadd[0] - pt) / (t - pt)
  239.                                 bezes = cspbezsplitatlength(e[-1][-1][:],end[0][0][:], nt)
  240.                                 e[-1][-1:] = bezes[:2]
  241.                                 end[0][0] = bezes[2]
  242.                                 pt = eadd.pop(0)
  243.                         e[-1].append(end[0].pop(0))
  244.                 start = s[:]
  245.                 end = e[:]
  246.             else:
  247.                 #which path has fewer segments?
  248.                 lengthdiff = numsegs(start) - numsegs(end)
  249.                 #swap shortest first
  250.                 if lengthdiff > 0:
  251.                     start, end = end, start
  252.                 #subdivide the shorter path
  253.                 for x in range(abs(lengthdiff)):
  254.                     maxlen = 0
  255.                     subpath = 0
  256.                     segment = 0
  257.                     for y in range(len(start)):
  258.                         for z in range(1, len(start[y])):
  259.                             leng = bezlenapprx(start[y][z-1], start[y][z])
  260.                             if leng > maxlen:
  261.                                 maxlen = leng
  262.                                 subpath = y
  263.                                 segment = z
  264.                     sp1, sp2 = start[subpath][segment - 1:segment + 1]
  265.                     start[subpath][segment - 1:segment + 1] = cspbezsplit(sp1, sp2)
  266.                 #if swapped, swap them back
  267.                 if lengthdiff > 0:
  268.                     start, end = end, start
  269.             
  270.             #break paths so that corresponding subpaths have an equal number of segments
  271.             s = [[]]
  272.             e = [[]]
  273.             while start and end:
  274.                 if start[0] and end[0]:
  275.                     s[-1].append(start[0].pop(0))
  276.                     e[-1].append(end[0].pop(0))
  277.                 elif end[0]:
  278.                     s.append(start.pop(0))
  279.                     e[-1].append(end[0][0])
  280.                     e.append([end[0].pop(0)])
  281.                 elif start[0]:
  282.                     e.append(end.pop(0))
  283.                     s[-1].append(start[0][0])
  284.                     s.append([start[0].pop(0)])
  285.                 else:
  286.                     s.append(start.pop(0))
  287.                     e.append(end.pop(0))
  288.     
  289.             if self.options.dup:
  290.                 steps = [0] + steps + [1]    
  291.             #create an interpolated path for each interval
  292.             group = self.document.createElement('svg:g')    
  293.             self.current_layer.appendChild(group)
  294.             for time in steps:
  295.                 interp = []
  296.                 #process subpaths
  297.                 for ssp,esp in zip(s, e):
  298.                     if not (ssp or esp):
  299.                         break
  300.                     interp.append([])
  301.                     #process superpoints
  302.                     for sp,ep in zip(ssp, esp):
  303.                         if not (sp or ep):
  304.                             break
  305.                         interp[-1].append([])
  306.                         #process points
  307.                         for p1,p2 in zip(sp, ep):
  308.                             if not (sp or ep):
  309.                                 break
  310.                             interp[-1][-1].append(interppoints(p1, p2, time))
  311.  
  312.                 #remove final subpath if empty.
  313.                 if not interp[-1]:
  314.                     del interp[-1]
  315.                 new = self.document.createElement('svg:path')
  316.  
  317.                 #basic style tweening
  318.                 if self.options.style:
  319.                     basestyle['opacity'] = tweenstylefloat('opacity',sst,est,time)
  320.                     if dostroke:
  321.                         basestyle['stroke-opacity'] = tweenstylefloat('stroke-opacity',sst,est,time)
  322.                         basestyle['stroke-width'] = tweenstyleunit('stroke-width',sst,est,time)
  323.                         basestyle['stroke'] = tweenstylecolor('stroke',sst,est,time)
  324.                     if dofill:
  325.                         basestyle['fill-opacity'] = tweenstylefloat('fill-opacity',sst,est,time)
  326.                         basestyle['fill'] = tweenstylecolor('fill',sst,est,time)
  327.                 new.setAttribute('style', simplestyle.formatStyle(basestyle))
  328.                 new.setAttribute('d', cubicsuperpath.formatPath(interp))
  329.                 group.appendChild(new)
  330.  
  331. e = Interp()
  332. e.affect()
  333.